#include "Map.h"

bool Map::Load(const std::string &path)
{
    std::ifstream file(path);
    if (!file.good()) return false;

    TileMap tileMap_temp;

    int idx_x = -1, idx_y = -1;

    std::string str_line;
    while (std::getline(file, str_line)) {
        str_line = TrimStr(str_line);
        if (str_line.empty()) {
            continue;
        }

        idx_x = -1, idx_y++;
        tileMap_temp.emplace_back();

        std::string str_tile;
        std::stringstream str_stream(str_line);
        while (std::getline(str_stream, str_tile, ',')) {
            idx_x++;
            Tile tile;
            LoadTileFromString(tile, str_tile);
            tileMap_temp[idx_y].emplace_back(tile);
        }
    }

    file.close();

    if (tileMap_temp.empty() || tileMap_temp[0].empty())
        return false;

    tileMap = tileMap_temp;

    GenerateMapCache();

    return true;
}

size_t Map::GetWidth() const
{
    if (tileMap.empty()) {
        return 0;
    }
    return tileMap[0].size();
}

size_t Map::GetHeight() const
{
    return tileMap.size();
}

const TileMap &Map::GetTileMap() const
{
    return tileMap;
}

const SDL_Point &Map::GetIdxHome() const
{
    return idxHome;
}

void Map::PlaceTower(const SDL_Point &idxTile)
{
    tileMap[idxTile.y][idxTile.x].hasTower = true;
}

const Map::SpawnerRouterPool &Map::GetSpawnerRouterPool() const
{
    return spawnerRouterPool;
}

std::string Map::TrimStr(const std::string &str)
{
    size_t begin_idx = str.find_first_not_of(" \t");
    if (begin_idx == std::string::npos) return "";
    size_t end_idx = str.find_last_not_of(" \t");
    size_t idx_range = end_idx - begin_idx + 1;

    return str.substr(begin_idx, idx_range);
}

void Map::LoadTileFromString(Tile &tile, const std::string &str)
{
    std::string str_tidy = TrimStr(str);

    std::string str_value;
    std::vector<int> values;
    std::stringstream str_stream(str_tidy);

    while (std::getline(str_stream, str_value, '\\')) {
        int value;
        try {
            value = std::stoi(str_value);
        }
        catch (const std::invalid_argument &) {
            value = -1;
        }
        values.push_back(value);
    }

    tile.terrian = (values.empty() || values[0] < 0) ? 0 : values[0];
    tile.decoration = (values.size() < 2) ? -1 : values[1];
    tile.direction = (Tile::Direction) ((values.size() < 3 || values[2] < 0) ? 0 : values[2]);
    tile.specialFlag = (values.size() <= 3) ? -1 : values[3];
}

void Map::GenerateMapCache()
{
    for (int y = 0; y < GetHeight(); y++) {
        for (int x = 0; x < GetWidth(); x++) {
            const Tile &tile = tileMap[y][x];
            if (tile.specialFlag < 0){
                continue;
            }
            if (tile.specialFlag == 0) {
                idxHome.x = x;
                idxHome.y = y;
            } else {
                spawnerRouterPool[tile.specialFlag] = Router(tileMap, {x, y});
            }
        }
    }
}
